#ifndef __M_LOOP_H__
#define __M_LOOP_H__
#include "buffer.hpp"
#include <mutex>
#include <thread>
#include <condition_variable>
#include <functional>

namespace mylog{
  using Functor = std::function<void(Buffer &)>;
  enum class AsyncType{
      ASYNC_SAFE,  //安全状态，表示缓冲区满了阻塞，避免资源耗尽的情况
      ASYNC_UNSAFE  //无限扩容，常用于性能测试
 };
  class AsyncLooper{
    public:
      using ptr = std::shared_ptr<AsyncLooper>;
      AsyncLooper(const Functor &cb, AsyncType type = AsyncType::ASYNC_SAFE):
        _callback(cb),_type(type),_stop(false),
        _thread(std::thread(&mylog::AsyncLooper::threadEntry, this))
       {}
      ~AsyncLooper()  {stop();}
      void stop(){
        _stop = true;  //将退出标志设置为true
        _cond_con.notify_all();   //唤醒所有消费者
        _thread.join();  //等待线程退出
      }
      void push(const char *data, size_t len){
        std::unique_lock<std::mutex> lock(_mutex);
        //条件变量，若缓冲区剩余空间大小大于数据长度，则可以添加数据
        if (_type == AsyncType::ASYNC_SAFE)
          _cond_pro.wait(lock, [&](){ return _pro_buf.writeAbleSize() >= len; });
        //能够走下来代表满足了条件，可以向缓冲区中添加数据
        _pro_buf.push(data, len);
        //唤醒消费者对缓冲区中的数据进行处理
        _cond_con.notify_one();
      }
    private:
      //线程入口函数
      void threadEntry(){
        while(1){
          {
            std::unique_lock<std::mutex> lock(_mutex);
            //退出标志被设置，且生产缓冲区已无数据，这时候再退出，否则有可能造成生产缓冲区中有数据，但没有处理
            if(_stop && _pro_buf.empty())  break;
            //线程要退出，或者生产缓冲区不为空，都直接等待成功
            _cond_con.wait(lock, [&](){ return _stop || !_pro_buf.empty(); });
            _con_buf.swap(_pro_buf); //交换两个缓冲区
           //唤醒生产者
            _cond_pro.notify_all();
          }
          //被唤醒后，对消费缓冲区的数据进行数据处理
          _callback(_con_buf);
          //初始化消费缓冲区
          _con_buf.reset();
        }
      }
    private:
      Functor _callback;  //具体对缓冲区数据进行处理的回调函数，由使用者传入
    private:
      AsyncType _type;
      bool _stop;  //异步工作器停止标志
      Buffer _pro_buf;  //生产缓冲区
      Buffer _con_buf;  //消费队列
      std::mutex _mutex;
      std::condition_variable _cond_pro;
      std::condition_variable _cond_con;
      std::thread _thread;  //异步工作器对应的工作线程
  };

}

#endif
